#define pr_fmt(fmt) "rtc: %s: " fmt, __func__

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/delay.h>
#include <linux/wakelock.h>
#include <linux/slab.h>
#include <linux/of_gpio.h>
#include <linux/irqdomain.h>



#define RTC_SECTION_LEN 0x07


#define PT7C43390_DEBUG
#ifdef PT7C43390_DEBUG
    #define debug_info(msg...) printk(msg);
#else
    #define debug_info(msg...)
#endif




struct pt7c43390 {
	int irq;
	struct i2c_client *client;
	struct mutex mutex;
	struct rtc_device *rtc;
	struct rtc_wkalrm alarm;
	struct wake_lock wake_lock;
};
static struct i2c_client *gClient;
struct pt7c43390 *pt7c43390;
extern void rtc_state1_set(void);

void pt7c43390_bin_swap(unsigned char * s){
    unsigned char temp1,temp2,temp3;
    temp1=((*s)&0x55)<<1;
    temp2=((*s)&0xaa)>>1;
    temp3=(temp1|temp2);
    temp1=(temp3&0x33)<<2;
    temp2=(temp3&0xcc)>>2;
    temp3=(temp1|temp2);
    temp1=(temp3&0x0f)<<4;
    temp2=(temp3&0xf0)>>4;
    (*s)=(temp1|temp2);
}

static int i2c_master_reg8_send(const struct i2c_client *client,
		const char reg, const char *buf, int count)
{
	struct i2c_adapter *adap = client->adapter;
	struct i2c_msg msg;
	int ret;
	char *tx_buf = kzalloc(count , GFP_KERNEL);
	if (!tx_buf)
		return -ENOMEM;
	// tx_buf[0] = reg;
	memcpy(tx_buf, buf, count);

	msg.addr = client->addr;
	msg.flags = client->flags;
	msg.len = count ;
	msg.buf = (char *)tx_buf;

	//printk("send 0:0x%x,1:0x%x,2:0x%x,3:0x%x,4:0x%x,5:0x%x,6:0x%x,\r\n",tx_buf[0],tx_buf[1],tx_buf[2],tx_buf[3],tx_buf[4],tx_buf[5],tx_buf[6]);		       

	ret = i2c_transfer(adap, &msg, 1);
	kfree(tx_buf);
	return (ret == 1) ? count : ret;
}

static int i2c_master_reg8_recv(const struct i2c_client *client,
		const char reg, char *buf, int count)
{
	struct i2c_adapter *adap = client->adapter;
	struct i2c_msg msgs[2];
	int ret;
	char reg_buf = reg;

	msgs[0].addr = client->addr;
	msgs[0].flags = client->flags;
	msgs[0].len = 1;
	msgs[0].buf = &reg_buf;

	msgs[1].addr = client->addr;
	msgs[1].flags = client->flags | I2C_M_RD;
	msgs[1].len = count;
	msgs[1].buf = buf;

	ret = i2c_transfer(adap, msgs, 2);
	//printk("%s,i2c_transfer_ret=%d\n", __func__, ret);
	//printk("i2c_transfer ret:%d\r\n",ret);

	return (ret == 2) ? count : ret;
}

static int pt7c43390_i2c_read_regs(struct i2c_client *client,
		u8 reg, u8 buf[], unsigned len)
{
	int ret;
	ret = i2c_master_reg8_recv(client, reg, buf, len);
    //printk("%s,i2c_master_reg8_recv_ret=%d\n", __func__, ret);
	return ret;
}

static int pt7c43390_i2c_set_regs(struct i2c_client *client,
		u8 reg, u8 const buf[], __u16 len)
{
	int ret;
	ret = i2c_master_reg8_send(client, reg, buf, (int)len);
	return ret;
}



static int pt7c43390_read_datetime(struct i2c_client *client, struct rtc_time *tm)
{
	struct pt7c43390 *pt7c43390 = i2c_get_clientdata(client);
	u8 regs[RTC_SECTION_LEN] = { 0, };
    // debug_info("%s\n", __func__);
	mutex_lock(&pt7c43390->mutex);
	pt7c43390_i2c_read_regs(client, 0x32, regs, RTC_SECTION_LEN);
	mutex_unlock(&pt7c43390->mutex);

	pt7c43390_bin_swap(&regs[0]);//年
	pt7c43390_bin_swap(&regs[1]);//月
	pt7c43390_bin_swap(&regs[2]);//日
	pt7c43390_bin_swap(&regs[3]);//周
	pt7c43390_bin_swap(&regs[4]);//时
	pt7c43390_bin_swap(&regs[5]);//分
	pt7c43390_bin_swap(&regs[6]);//秒
	//printk("0:0x%x,1:0x%x,2:0x%x,3:0x%x,4:0x%x,5:0x%x,6:0x%x,\r\n",regs[0],regs[1],regs[2],regs[3],regs[4],regs[5],regs[6]);

	tm->tm_sec = bcd2bin(regs[0x06] & 0x7F);
	tm->tm_min = bcd2bin(regs[0x05] & 0x7F);
	tm->tm_hour = bcd2bin(regs[0x04] & 0x3F);
	tm->tm_mday = bcd2bin(regs[0x02] & 0x3F);
	tm->tm_wday = bcd2bin(regs[0x03] & 0x07);
	tm->tm_mon = bcd2bin(regs[0x01] & 0x1F);
	tm->tm_mon -= 1;
	tm->tm_year = bcd2bin(regs[0x00] & 0xFF);


	if (regs[1] & 0x80)
		tm->tm_year += 1900;
	else
		tm->tm_year += 2000;

	tm->tm_year -= 1900;
	if (tm->tm_year < 0)
		tm->tm_year = 0;
	tm->tm_isdst = 0;

	// debug_info("%4d-%02d-%02d(%d) %02d:%02d:%02d\n",
	// 		1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
	// 		tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	return 0;

}

static int pt7c43390_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
	return pt7c43390_read_datetime(to_i2c_client(dev), tm);
}

static int pt7c43390_set_time(struct i2c_client *client, struct rtc_time *tm)
{
	struct pt7c43390 *pt7c43390 = i2c_get_clientdata(client);
	u8 regs[RTC_SECTION_LEN] = { 0, };
	u8 mon_day;
	// debug_info("%s\n", __func__);
	// debug_info("xxxxxxx%4d-%02d-%02d(%d) %02d:%02d:%02d\n",
	// 		1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
	// 		tm->tm_wday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	// debug_info("%s\n", __func__);

	if (tm->tm_year < 100 || tm->tm_year >= 200)
	return -EINVAL;

	// pt7c43390_bin_swap((unsigned char *)&(tm->tm_year ));//年6
	// pt7c43390_bin_swap((unsigned char *)&(tm->tm_mon ));//月5
	// pt7c43390_bin_swap((unsigned char *)&tm->tm_wday);//日4
	// pt7c43390_bin_swap((unsigned char *)&tm->tm_mday);//周3
	// pt7c43390_bin_swap((unsigned char *)&tm->tm_hour);//时2
	// pt7c43390_bin_swap((unsigned char *)&tm->tm_min);//分1
	// pt7c43390_bin_swap((unsigned char *)&tm->tm_sec);//秒0

	// regs[0] = 0x12;//月
	// regs[1] = 0x02;//日
	// regs[2] = 0x03;//周
	// regs[3] = 0x04;//时
	// regs[4] = 0x05;//分
	// regs[5] = 0x06;//秒
	// regs[6] = 0x09;
	
	// printk("0:0x%x,1:0x%x,2:0x%x,3:0x%x,4:0x%x,5:0x%x,6:0x%x,\r\n",regs[0],regs[1],regs[2],regs[3],regs[4],regs[5],regs[6]);		       

	// pt7c43390_bin_swap(&regs[0x00]);//年6
	// pt7c43390_bin_swap(&regs[0x01]);//月5
	// pt7c43390_bin_swap(&regs[0x02]);//日4
	// pt7c43390_bin_swap(&regs[0x03]);//周3
	// pt7c43390_bin_swap(&regs[0x04]);//时2
	// pt7c43390_bin_swap(&regs[0x05]);//分1
	// pt7c43390_bin_swap(&regs[0x06]);//秒0
	mon_day = rtc_month_days((tm->tm_mon), tm->tm_year + 1900);

		if (tm->tm_sec >= 60 || tm->tm_sec < 0)
		regs[0x06] = bin2bcd(0x00);
	else
		regs[0x06] = bin2bcd(tm->tm_sec);

	if (tm->tm_min >= 60 || tm->tm_min < 0)
		regs[0x05] = bin2bcd(0x00);
	else
		regs[0x05] = bin2bcd(tm->tm_min);

	if (tm->tm_hour >= 24 || tm->tm_hour < 0)
		regs[0x04] = bin2bcd(0x00);
	else
		regs[0x04] = bin2bcd(tm->tm_hour);


	if ((tm->tm_mday) > mon_day)
		regs[0x02] = bin2bcd(mon_day);
	else if ((tm->tm_mday) > 0)
		regs[0x02] = bin2bcd(tm->tm_mday);
	else if ((tm->tm_mday) <= 0)
		regs[0x02] = bin2bcd(0x01);

	if (tm->tm_year >= 200)
		regs[0x00] = bin2bcd(99);
	else if (tm->tm_year >= 100)
		regs[0x00] = bin2bcd(tm->tm_year - 100);
	else if (tm->tm_year >= 0) {
		regs[0x00] = bin2bcd(tm->tm_year);
		regs[0x01] |= 0x80;
	} else {
		regs[0x00] = bin2bcd(0);
		regs[0x01] |= 0x80;
	}
	regs[0x03] = bin2bcd(tm->tm_wday);
	regs[0x01] = (regs[0x01] & 0x80) | (bin2bcd(tm->tm_mon + 1) & 0x7F);
	

 	

	pt7c43390_bin_swap(&regs[0x00]);//年6
	pt7c43390_bin_swap(&regs[0x01]);//月5
	pt7c43390_bin_swap(&regs[0x02]);//日4
	pt7c43390_bin_swap(&regs[0x03]);//周3
	pt7c43390_bin_swap(&regs[0x04]);//时2
	pt7c43390_bin_swap(&regs[0x05]);//分1
	pt7c43390_bin_swap(&regs[0x06]);//秒0
	//	regs[0x04]= regs[0x04]|(0x01);
	//	printk("regs[0x04]:0x%x\r\n",regs[0x04]);
	//printk("0:0x%x,1:0x%x,2:0x%x,3:0x%x,4:0x%x,5:0x%x,6:0x%x,\r\n",regs[0],regs[1],regs[2],regs[3],regs[4],regs[5],regs[6]);		       

	
	mutex_lock(&pt7c43390->mutex);
	pt7c43390_i2c_set_regs(client, 0x32, regs, RTC_SECTION_LEN);
	mutex_unlock(&pt7c43390->mutex);

	return 0;
}

static int pt7c43390_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
	return pt7c43390_set_time(to_i2c_client(dev), tm);
}




static const struct rtc_class_ops pt7c43390_rtc_ops = {
	.read_time  = pt7c43390_rtc_read_time,
	.set_time   = pt7c43390_rtc_set_time,
	.read_alarm = NULL,
	.set_alarm  = NULL,
	.ioctl      = NULL,
	.proc       = NULL
};


static int pt7c43390_probe(struct i2c_client *client,
		const struct i2c_device_id *id)
{
	
	int rc = 0;
	
	
	struct rtc_device *rtc;

	printk("%s,%d\r\n",__FUNCTION__,__LINE__);

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
		return -ENODEV;

	pt7c43390 = devm_kzalloc(&client->dev, sizeof(*pt7c43390), GFP_KERNEL);
	if (!pt7c43390)
		return -ENOMEM;

	gClient = client;
	pt7c43390->client = client;

	mutex_init(&pt7c43390->mutex);

	i2c_set_clientdata(client, pt7c43390);

	rtc_state1_set();

	 rtc = rtc_device_register(client->name, &client->dev,
			&pt7c43390_rtc_ops, THIS_MODULE);

	if (IS_ERR(rtc)) {
		rc = PTR_ERR(rtc);
		rtc = NULL;
		goto exit;
	}
	pt7c43390->rtc = rtc;


	return 0;

exit:

	return rc;
}

static int  pt7c43390_remove(struct i2c_client *client)
{
	struct pt7c43390 *pt7c43390 = i2c_get_clientdata(client);
	mutex_destroy(&pt7c43390->mutex);
	devm_kfree(&client->dev,pt7c43390);
	rtc_device_unregister(pt7c43390->rtc);
	return 0;
}


static const struct i2c_device_id pt7c43390_id[] = {
	{ "xagrtc",0},
	{ }
};
MODULE_DEVICE_TABLE(i2c, pt7c43390_id);

static struct of_device_id rtc_dt_ids[] = {
	{ .compatible = "xagrtc" },
	{},
};

struct i2c_driver pt7c43390_driver = {
	.driver     = {
		.name   = "xagrtc",
		.owner  = THIS_MODULE,
		.of_match_table = of_match_ptr(rtc_dt_ids),
	},
	.probe      = pt7c43390_probe,
	.remove     = pt7c43390_remove,
	.id_table   = pt7c43390_id,
};

static int __init pt7c43390_init(void)
{
	printk("%s,%d\r\n",__FUNCTION__,__LINE__);
	return i2c_add_driver(&pt7c43390_driver);
}

static void __exit pt7c43390_exit(void)
{
	printk("%s,%d\r\n",__FUNCTION__,__LINE__);
	i2c_del_driver(&pt7c43390_driver);
}

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xag");

module_init(pt7c43390_init);
module_exit(pt7c43390_exit);
